const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const OfflinePlugin = require('offline-plugin');
const { HashedModuleIdsPlugin } = require('webpack').ids;
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');

module.exports = require('./webpack.base.babel')({
    mode: 'production',

    // 在生产环境中，跳过所有的热重新加载的东西
    entry: [
        require.resolve('react-app-polyfill/ie11'), // 让ie11兼容react app
        path.join(process.cwd(), 'app/app.js'), // 入口文件 app/app.js
    ],

    // 通过向已编译资产添加内容哈希（而不是编译哈希）来利用长期缓存
    output: {
        filename: '[name].[chunkhash].js',
        chunkFilename: '[name].[chunkhash].chunk.js',
    },

    optimization: {
        // 告知 webpack 使用 TerserPlugin 或其它在 optimization.minimizer 定义的插件压缩 bundle。
        minimize: true,
        // 允许你通过提供一个或多个定制过的 TerserPlugin 实例， 覆盖默认压缩工具(minimizer)。
        minimizer: [
            new TerserPlugin({
                // Terser 压缩配置 。
                terserOptions: {
                    format: {
                        comments: false, // 选项指定是否保留注释。
                    },
                    warnings: false,
                    compress: {
                        // 对二进制节点应用某些优化, 比如 !(a <= b) → a > b
                        comparisons: false,
                        // 打包时自动去除console.log
                        pure_funcs: ["console.log"]
                    },
                    parse: {},
                    mangle: true,
                    output: {
                        comments: false,
                        ascii_only: true,
                    },
                },
                // 使用多进程并发运行以提高构建速度。并发运行可以显著提高构建速度，因此强烈建议添加此配置 。
                parallel: true,
                cache: true, // 启用文件缓存。
                sourceMap: true,
                extractComments: false, // 禁用剥离注释功能。
            }),
        ],
        // 告知 webpack 将 process.env.NODE_ENV 设置为一个给定字符串。
        nodeEnv: 'production',
        // 告知 webpack 去辨识 package.json 中的 副作用 标记或规则，
        // 以跳过那些当导出不被使用且被标记不包含副作用的模块。
        sideEffects: true,
        // 告知 webpack 去寻找模块图形中的片段，哪些是可以安全地被合并到单一模块中。
        concatenateModules: true,
        // 值 "single" 会创建一个在所有生成 chunk 之间共享的运行时文件。
        runtimeChunk: 'single',
        // SplitChunks插件是Webpack中一个提取或分离代码的插件，
        // 主要作用是提取公共代码，防止代码被重复打包，拆分过大的js文件，合并零散的js文件。
        splitChunks: {
            chunks: 'all', // 不管异步加载还是同步加载的模块都提取出来，打包到一个文件中。
            maxInitialRequests: 10, // 按需加载时的最大并行请求数。
            minSize: 0, // 生成 chunk 的最小体积（以 bytes 为单位）。
            // 缓存组可以继承和/或覆盖来自 splitChunks.* 的任何选项。
            // 但是 test、priority 和 reuseExistingChunk 只能在缓存组级别上进行配置。
            cacheGroups: {
                vendor: {
                    // 控制此缓存组选择的模块。省略它会选择所有模块。
                    // 它可以匹配绝对模块资源路径或 chunk 名称。
                    // 匹配 chunk 名称时，将选择 chunk 中的所有模块。
                    test: /[\\/]node_modules[\\/]/,
                    // 拆分 chunk 的名称。
                    // 提供字符串或函数使你可以使用自定义名称。
                    // 指定字符串或始终返回相同字符串的函数会将所有常见模块和 vendor 合并为一个 chunk。
                    // 这可能会导致更大的初始下载量并减慢页面加载速度。
                    name(module) {
                        const packageName = module.context.match(
                            /[\\/]node_modules[\\/](.*?)([\\/]|$)/,
                        )[1];
                        return `npm.${packageName.replace('@', '')}`;
                    },
                },
            },
        },
    },

    plugins: [
        // 压缩并优化index.html
        new HtmlWebpackPlugin({
            // webpack到模板的相对或绝对路径。
            template: 'app/index.html',
            // 控制是否以及以何种方式压缩。
            minify: {
                removeComments: true,
                collapseWhitespace: true,
                removeRedundantAttributes: true,
                useShortDoctype: true,
                removeEmptyAttributes: true,
                removeStyleLinkTypeAttributes: true,
                keepClosingSlash: true,
                minifyJS: true,
                minifyCSS: true,
                minifyURLs: true,
            },
            // 根据scriptLoading选项将资源添加到head/body。
            inject: true,
        }),


        new OfflinePlugin({
            relativePaths: false,
            publicPath: '/',
            appShell: '/',
            caches: {
                main: [':rest:'],
                // 所有标记为“additional”的块，在main文件之后加载，不阻止SW安装。
                additional: ['*.chunk.js'],
            },

            // 删除关于“additional”用法的警告
            safeToUseOptionalCaches: true,
        }),

        new CompressionPlugin({
            algorithm: 'gzip', // 压缩算法/函数
            test: /\.js$|\.css$|\.html$/,
            threshold: 10240, // 仅处理大于此大小的资源（字节）
            minRatio: 0.8, // 只处理压缩比此比率更好的资源
        }),

        new HashedModuleIdsPlugin({
            hashFunction: 'sha256', // 生成哈希时要使用的方法
            hashDigest: 'hex', // 生成哈希时要使用的编码
            hashDigestLength: 20, // 要使用的哈希摘要的前缀长度
        }),
    ],

    performance: {
        // 筛选出自己需要检测体积的包类型
        assetFilter: assetFilename =>
            !/(\.map$)|(^(main\.|favicon\.))/.test(assetFilename),
    },
});